使用 Ruptures 识别时间序列数据中的变化点
时间序列数据 在各个领域中都占据着重要地位,从金融市场到生产制造,都需要对时间序列数据进行分析和监测。其中一个关键任务是识别时间序列数据中的变化点,这些变化点可能代表了重要的事件或趋势转折点。例如之前分享过 金融研究 | 央行货币政策文本相似度计算与可视化 , 仅仅构造了相似度时序数据, 但是如果要做让程序自动识别政策变化时间点, 还需要今日分享的内容。
为了解决这一问题,Ruptures 库是一个非常强大的工具,它提供了多种算法,可用于检测时间序列数据的变化点。本文将介绍如何使用 Ruptures 库来解决时间序列数据分析中的变化点检测问题。
一、问题场景
在各种应用场景中,需要识别时间序列数据中的变化点,例如:
金融市场:检测股票价格中的趋势转折点,以指导投资决策。 生产制造:监测生产线上的设备状态变化,及时发现问题并采取措施维护。 气象数据:发现天气数据中的异常变化,如风暴的到来或气温剧烈波动。 网络流量:检测网络流量中的异常行为,可能是网络攻击的迹象。
在这些场景下,Ruptures 库可以帮助我们识别变化点,从而更好地理解时间序列数据的特点。
二、Ruptures 库介绍
Ruptures 库是一个用于信号分割和变化点检测的 Python 库,它提供了多种算法和工具,可用于处理不同类型的时间序列数据。
以下是 Ruptures 库的一些关键特点:
多种算法支持:Ruptures 提供了多种变化点检测算法,包括 Pelt、Binary Segmentation、Window-based Methods 等,适用于不同类型的时间序列数据和问题。 简单易用:库的 API 设计简洁,容易上手,用户可以轻松地进行变化点检测任务。 高性能:Ruptures 经过优化,能够处理大规模的时间序列数据集,同时具有较低的计算复杂度。
三、常用算法
下面是 Ruptures 库中常用的一些变化点检测算法:
Pelt (Pruned Exact Linear Time):Pelt算法是一种基于动态规划的算法,适用于多个变化点的检测任务。它的优点在于其精确性和高效性,通常能够找到全局最优的变化点位置。Pelt算法通过将时间序列数据划分为多个分段,使得每个分段内的变化点数目最小化,从而找到最优的分段方式。 Binary Segmentation (BS):Binary Segmentation算法是一种简单而有效的分割方法,通过迭代地将时间序列数据分为两个部分来检测变化点。该算法的计算复杂度较低,适用于中等规模的数据集。主要缺点是可能会导致分段的粒度过粗。 Window-based Methods:这些方法使用滑动窗口的方式来检测时间序列数据中的变化点。窗口会在时间序列上滑动,对窗口内的数据进行分析,然后根据某种准则来确定窗口内是否存在变化点。优点是简单易懂,但需要调整窗口大小和准则参数。 Bottom-Up Methods:Bottom-Up方法从小的分段开始,逐渐合并以检测变化点。它从最小的分段(每个数据点都是一个分段)开始,然后合并相邻的分段,直到满足某种准则为止。优点在于能够处理多个变化点,但计算复杂度较高。
四、实验
4.1 导入包
导入本文需要的包, 使得matplotlib支持中文,绘制高清图;
import matplotlib.pyplot as plt
import ruptures as rpt
import matplotlib
#绘制高清图
import matplotlib_inline
matplotlib_inline.backend_inline.set_matplotlib_formats('png', 'svg')
#支持中文
import platform
system = platform.system() # 获取操作系统类型
if system == 'Windows':
font = {'family': 'SimHei'}
elif system == 'Darwin':
font = {'family': 'Arial Unicode MS'}
else:
font = {'family': 'sans-serif'}
matplotlib.rc('font', **font) # 设置全局字体
4.2 生成实验数据
# 生成示例时间序列数据
n_samples, dim, sigma = 1000, 1, 1
n_bkps = 4 # 假设有4个变化点
signal, bkps = rpt.pw_constant(n_samples, dim, n_bkps, noise_std=sigma)
print(signal.shape)
print(bkps)
signal
Run
(1000, 1)
[203, 408, 603, 789, 1000]
array([[-3.72701244],
[-3.5992294 ],
[-5.37957509],
[-3.58538893],
[-3.19651608],
[-4.48293727],
[-1.58468434],
...
[11.66240607],
[12.74493248],
[12.54103519],
[12.41838346],
[13.94702808],
[13.36888594],
[12.20846486],
[11.68636148]])
ruptures为我们生成的实验数据signal是一个长度为1000的array型数据。生成的变化点bkps的位置序列[203, 408, 603, 789, 1000]。数据不够直观,我们可视化一下
# 创建时间序列图
plt.figure(figsize=(12, 6))
plt.plot(signal, lw=2, label='时间序列数据')
plt.legend()
#保存
plt.savefig('ts-data.png', dpi=200)
# 显示
plt.show()
从上图可以清楚的看到,ruptures为我们生成了1000个点,大致有4个变化点,将数据分成了五部分。现在我们使用ruptures为我们识别变化点
4.3 识别变化点
Pelt算法是Ruptures库中的一种高效而准确的变化点检测算法,它的全称是Pruned Exact Linear Time(修剪的线性时间精确算法)。它的性能取决于成本函数的选择和 pen参数的调整,pen 参数的全称是 Penalty,它代表了在检测到变化点时的成本或惩罚值。这里将 pen 设置为 10
# 使用 Ruptures 库进行变化点检测
algo = rpt.Pelt(model="rbf").fit(signal)
result = algo.predict(pen=10)
# 绘制结果
rpt.display(signal, bkps, result)
plt.title("变化点检测示例")
#保存图
plt.savefig('change-point.png', dpi=200)
#显示
plt.show()
4.4 关于pen
更具体地说,pen 参数的值越大,算法就会倾向于检测更少的变化点,而值越小,算法就会倾向于检测更多的变化点。
通常情况下,您可以根据自己的数据和问题来调整pen参数的值。以下是一些常见的情况和建议:
如果您希望检测到较少的变化点,以捕捉主要的趋势转折点,可以选择较大的pen值。 如果您希望检测到更多的变化点,以捕捉数据中的细微变化,可以选择较小的pen值。 如果您不确定要选择哪个pen值,可以尝试多个不同的值,然后根据结果的质量和实际需求来选择最合适的pen值。
在实践中,调整pen参数通常需要一些试验和经验,因为最佳的pen值取决于您的数据和分析目标。您可以尝试不同的pen值,然后根据检测结果和领域知识来选择最适合的参数值。